package simple.flow.config.shiro;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;

/**
 * shiro 配置类
 *
 * @author lhd
 * @since 2025/4/2 17:37
 */

@Slf4j
@Configuration
public class ShiroConfig {

    /**
     * 配置 SecurityManager
     */
    @Bean("securityManager")
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultWebSecurityManager securityManager(UserRealm userRealm) {
        // 1.创建 defaultWebSecurityManager 对象
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 2.将 userRealm 存入 defaultWebSecurityManager 对象
        securityManager.setRealm(userRealm);

        // 3. 关闭 Shiro 自带的 session
        DefaultSessionManager sessionManager = new DefaultSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(false);
        securityManager.setSessionManager(sessionManager);

        // 4. 自定义缓存实现,使用redis
        //securityManager.setCacheManager(redisCacheManager());

        // 5.返回
        return securityManager;
    }


    /**
     * 配置 Shiro 过滤器工厂
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 配置过滤器链 过滤顺序从上到下
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();

        // 登录相关过滤配置
        chainDefinition.addPathDefinition("/sys/user/login", "anon");
        chainDefinition.addPathDefinition("/sys/user/register", "anon");
        chainDefinition.addPathDefinition("/sys/user/logout", "logout");

        // 开放接口过滤配置
        chainDefinition.addPathDefinition("/open/**", "anon");

        // 自定义全局过滤器(使用token进行认证)
        Map<String, Filter> filters = new HashMap<>(1);
        filters.put("jwt", new JwtFilter());
        shiroFilterFactoryBean.setFilters(filters);
        chainDefinition.addPathDefinition("/**", "jwt");

        // 设置过滤器链
        shiroFilterFactoryBean.setFilterChainDefinitionMap(chainDefinition.getFilterChainMap());
        return shiroFilterFactoryBean;
    }


    /**
     * Shiro生命周期处理器
     * 确保 Bean 的初始化顺序符合 Shiro 生命周期管理的要求
     * 反例：SecurityManager 可能先于 Realm 初始化
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


    /**
     * 开启shiro aop注解支持
     * 自动为匹配的 Bean 创建代理，从而支持 Shiro 的注解驱动权限控制（如 @RequiresRoles、@RequiresPermissions 等）
     * 说明: 配置作用就是让Spring AOP 拦截 Shiro 的注解，从而实现权限控制
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);

        // 防止匹配到其他的advisor
        creator.setUsePrefix(true);
        creator.setAdvisorBeanNamePrefix("shiro_advisor");
        return creator;
    }


    /**
     * shiro aop注解权限验证逻辑支持
     * 负责将 Shiro 的权限检查逻辑与 Spring AOP 框架连接起来
     * 说明: 配置作用在Spring AOP 拦截后，将securityManager中的权限校验逻辑交给Spring AOP判断，实现注解权限功能
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
